package com.vdongchina.miniapp.service.impl;

import com.google.common.collect.Lists;
import com.vdongchina.miniapp.commons.MiniAppException;
import com.vdongchina.miniapp.commons.RedissonRedisOps;
import com.vdongchina.miniapp.configure.AppProperties;
import com.vdongchina.miniapp.constants.MiniAppConstants;
import com.vdongchina.miniapp.http.dto.*;
import com.vdongchina.miniapp.http.service.IAppHttpService;
import com.vdongchina.miniapp.service.IAppUserService;
import com.vdongchina.miniapp.utils.LoginStatSign;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import retrofit2.Call;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Copyright (C)
 * FileName: MiniAppService
 * Author:   maokai
 * Date:     2021/8/12 14:46
 * Description:
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class IAppUserServiceImpl implements IAppUserService {
    private final IAppHttpService appHttpService;
    private final AppProperties appProperties;
    private final RedissonRedisOps redissonRedisOps;

    /**
     * 解析用户信息
     *
     * @param code
     * @param anonymousCode
     */
    @Override
    public JsCodeResp jscode2session(String code, String anonymousCode) throws MiniAppException {
        String appId = appProperties.getAppId();
        String secret = appProperties.getSecret();
        JsCodeResp resp = null;
        Call<JsCodeResp> appRespBodyCall = appHttpService.jscode2session(appId, secret, code, anonymousCode);
        resp = this.appHttpService.parserResult(appRespBodyCall);
        if (Objects.isNull(resp)) {
            throw new MiniAppException("解析用户信息返回空");
        }
        resp.success();
        String sessionKeyValue = resp.getSessionKey();
        String openid = resp.getOpenid();
        String sessionKey = redissonRedisOps.contact(MiniAppConstants.KeyConst.SESSION_KEY, openid);
        redissonRedisOps.setValue(sessionKey, sessionKeyValue, 7200, TimeUnit.SECONDS);
        return resp;
    }

    /**
     * 获取token
     */
    @Override
    public String getAccessToken(String appId) throws MiniAppException {
        String secret = null;
        if (StringUtils.isBlank(appId)) {
            appId = appProperties.getAppId();
            secret = appProperties.getSecret();
        }
        String key = redissonRedisOps.contact(MiniAppConstants.KeyConst.ACCESS_TOKEN, appId);
        String access_token = redissonRedisOps.getValue(key);
        if (StringUtils.isNotBlank(access_token)) {
            return access_token;
        }
        final String grantType = "client_credential";
        Call<AppTokenResp> miniAppToken = appHttpService.getMiniAppToken(appId, secret, grantType);
        AppTokenResp body = appHttpService.parserResult(miniAppToken);
        if (Objects.isNull(body)) {
            throw new MiniAppException("获取token返回空");
        }
        body.success();
        access_token = body.getAccessToken();
        int expires_in = body.getExpiresIn();
        redissonRedisOps.setValue(key, access_token, expires_in, TimeUnit.SECONDS);
        log.info(access_token);
        return access_token;
    }


    /**
     * 以 key-value 形式存储用户数据到小程序平台的云存储服务。
     * 若开发者无内部存储服务则可接入，免费且无需申请。
     * 一般情况下只存储用户的基本信息，禁止写入大量不相干信息。
     *
     * @param accessToken
     * @param openid
     * @param body
     */
    @Override
    public boolean setUserStorage(String accessToken,
                                  String openid,
                                  List<Map<String, Object>> body) throws MiniAppException {
        String sessionKey = redissonRedisOps.getValue(redissonRedisOps.contact(MiniAppConstants.KeyConst.SESSION_KEY, openid));
        if (StringUtils.isBlank(sessionKey)) {
            throw new MiniAppException("sessionKey is empty");
        }
        String sigMethod = LoginStatSign.SIGN_METHOD;
        String signature = LoginStatSign.digest(body, sessionKey);
        Call<AppResp> appRespCall = appHttpService.setUserStorage(accessToken, openid, signature, sigMethod, body);
        AppResp appResp = appHttpService.parserResult(appRespCall);
        if (Objects.isNull(appResp)) {
            throw new MiniAppException("存储服务返回空");
        }
        appResp.success();
        return true;
    }

    /**
     * 删除存储到字节跳动的云存储服务的 key-value 数据。
     * 当开发者不需要该用户信息时，需要删除，以免占用过大的存储空间。
     *
     * @param accessToken
     * @param openid
     * @param body
     */
    @Override
    public boolean removeUserStorage(String accessToken,
                                     String openid,
                                     List<String> body) throws MiniAppException {
        String sessionKey = redissonRedisOps.getValue(redissonRedisOps.contact(MiniAppConstants.KeyConst.SESSION_KEY, openid));
        if (StringUtils.isBlank(sessionKey)) {
            throw new MiniAppException("sessionKey is empty");
        }
        String sigMethod = LoginStatSign.SIGN_METHOD;
        String signature = LoginStatSign.digest(body, sessionKey);
        Call<AppResp> appRespCall = appHttpService.removeUserStorage(accessToken, openid, signature, sigMethod, body);
        AppResp appResp = appHttpService.parserResult(appRespCall);
        if (Objects.isNull(appResp)) {
            throw new MiniAppException("存储服务删除操作返回空");
        }
        appResp.success();
        return true;
    }

    /**
     * 获取小程序/小游戏的二维码。该二维码可通过任意 app 扫码打开，
     * 能跳转到开发者指定的对应字节系 app 内拉起小程序/小游戏，
     * 并传入开发者指定的参数。通过该接口生成的二维码，永久有效，暂无数量限制。
     *
     * @param request
     */
    @Override
    public byte[] qrcode(QrcodeRequest request) throws MiniAppException {
        Call<byte[]> qrcode = appHttpService.qrcode(request);
        byte[] bytes = appHttpService.parserResult(qrcode);
        return bytes;
    }

    /**
     * 内容安全检测
     *
     * @param accessToken
     * @param body
     */
    @Override
    public List<AntidirtResp.DataResult> antidirt(String accessToken, AntidirtRequest body) throws MiniAppException {
        Call<AntidirtResp> antidirtRespCall = appHttpService.antidirt(accessToken, body);
        AntidirtResp antidirtResp = appHttpService.parserResult(antidirtRespCall);
        if (Objects.isNull(antidirtResp)) {
            throw new MiniAppException("内容安全检测返回空");
        }
        List<AntidirtResp.DataResult> data = antidirtResp.getData();
        if (CollectionUtils.isEmpty(data)) {
            return Lists.newArrayList();
        }
        return data;
    }

    /**
     * 图片检测 V2
     *
     * @param body
     */
    @Override
    public List<ImageResp.Predicts> censorImage(CensorImageRequest body) throws MiniAppException {
        Call<ImageResp> imageRespCall = appHttpService.censorImage(body);
        ImageResp imageResp = appHttpService.parserResult(imageRespCall);
        if (Objects.isNull(imageResp)) {
            throw new MiniAppException("图片检测安全检测返回空");
        }
        imageResp.success();
        List<ImageResp.Predicts> predicts = imageResp.getPredicts();

        return predicts;
    }

    /**
     * 订阅消息推送
     *
     * @param body
     */
    @Override
    public boolean subscribeNotification(SubscribeNotifyRequest body) throws MiniAppException {
        Call<SettleResp> settleRespCall = appHttpService.subscribeNotification(body);
        SettleResp settleResp = appHttpService.parserResult(settleRespCall);
        if (Objects.isNull(settleResp)) {
            throw new MiniAppException("订阅消息推送返回异常");
        }
        settleResp.success();
        return true;
    }
}
